#pragma once

// class declarations
class CogProductionStats;
class CogEvalThread;
class CogEvolver;

// -----

#include "CogParameters.h"
#include "CogGrammar.h"

// ------------------------------------------------------------------------------------------

class CogProductionStats
{
public:
	CogProductionStats();

	size_t instanceCount;
	double instanceUsageMean;
	double instanceUsageStd;
	double instanceUsageMax;
	double instanceAgeMean;
	double instanceAgeStd;
	double instanceAgeMax;
	double ntSizeMean;
	double ntSizeStd;
	double ntSizeMin;
	double ntSizeMax;

};

// ------------------------------------------------------------------------------------------

class CogEvalThread
{
public:
    CogEvalThread(CogEvolver* _pEvolver, size_t _stopIndex);
	void operator()();
    
private:
	// data:
    CogEvolver* pEvolver;
	size_t stopIndex;
};

// ------------------------------------------------------------------------------------------

class CogEvolver
{
public:
	CogEvolver() : recordStats(false) { }

	// inline methods:
	void instantiateSolution(CogProduction* pOldRule);
	void addSolution(CogProduction* pNewRule);
	void evaluate(size_t index);
	void evaluateSolutions() { evaluateSolutions(0, numSolutions); }
	void computeSolutionSize();
	size_t getGeneration() { return currentGeneration; }
	
	void initialize();
	void evolve();
	void evaluateSolutions(size_t startIndex, size_t endIndex);
	void solutionsToStream(ostream &out) const;
	void statsToStream(ostream &out) const;

	// data:
	CogGrammar grammar;
	CogParameters parameters;
	bool recordStats; // set via command-line args

protected:
	void evolveStep();
	void allocSolutions();
	void fillSolutions(size_t wantedSize);
	void rankSortSolutions();

	void setupStats();
	void computeAllSolutionStats(vector<vector<double>>& statMatrix, size_t startIndex, size_t stopIndex, const string& tag);
	void computeGlobalStats(vector<vector<double>>& statMatrix);
	void addClockStat(vector<vector<double>>& statMatrix, double time, size_t index = 1);
	void addFailedStat(vector<vector<double>>& statMatrix, size_t invalidCount);
	void printSolutionStats(size_t index, const string& tag);
	void collectStats(CogProductionStats& stats, CogProductionPool* pPool);
	void collectStats2(CogProductionStats& stats, CogProductionPool* pPool);

	vector<CogSolution>* pSolutions;
	vector<CogSolution> parents, offspring;
	vector<vector<double>> eliteStats, offspringStats, globalStats;
	uint32_t currentGeneration;
	size_t numSolutions;

	// thread management
	friend class CogEvalThread;
	boost::mutex evalMutex;
	size_t currentEvalIndex;
};

// ------------------------------------------------------------------------------------------
// inlined:

inline
void CogEvolver::instantiateSolution(CogProduction* pOldRule)
{
	CogProduction* pNewRule = grammar.forceInstantiate(pOldRule);
	pNewRule->lhs.flag |= COGSF_STARTING;
	addSolution(pNewRule);
}

inline
void CogEvolver::addSolution(CogProduction* pNewRule)
{
	(*pSolutions)[numSolutions++].pRule = pNewRule;
}

inline
void CogEvolver::evaluate(size_t index)
{
	parameters.objectives.evaluate((*pSolutions)[index]);
}

inline
void CogEvolver::computeSolutionSize()
{
	for (size_t i = 0; i < numSolutions; i++)
	{
		(*pSolutions)[i].pRule->computeSize();
	}
}

// ------------------------------------------------------------------------------------------
